#include "spiflash.h"

/* spiflash 芯片相关特性 */
spiflash_attr s_spiflash_attr;

/* --------------------------------------------------------------------- */
/* 发送写使能命令 */
static void spiflash_write_enable(void)
{
    uint8_t txbuf[1] = {0}, rxbuf[1] = {0};

    {
        SPIFLASH_CS_LOW();        /* 使能片选 */

        txbuf[0] = SPIFLASH_CMD_EWR;   /* 写使能命令 */

        /* 启动SPI数据传输 */
        spi_transfer(txbuf, rxbuf, 1);

        SPIFLASH_CS_HIGH();        /* 失能片选 */
    }
}

/* 采用循环查询的方式等待器件内部写操作完成 */
static void spiflash_wait_write_end(void)
{
    uint8_t txbuf[2] = {0}, rxbuf[2] = {0};

    while (1)
    {
        {
            SPIFLASH_CS_LOW();        /* 使能片选 */

            txbuf[0] = SPIFLASH_CMD_RDSR;   /* 写使能命令 */
            txbuf[1] = SPIFLASH_CMD_DUMMY;

            /* 启动SPI数据传输 */
            spi_transfer(txbuf, rxbuf, 2);

            SPIFLASH_CS_HIGH();        /* 失能片选 */
        }

        /* 判断状态寄存器的 WIP 位状态 */
        if ((rxbuf[1] & SPIFLASH_REG_SR_WIP) == 1)
        {
            break;
        }
    }
}

/* 获得 spiflash 芯片ID */
static uint32_t spiflash_get_id(void)
{
    uint32_t id;
    uint8_t  id1, id2, id3;
    uint8_t  txbuf[1] = {0}, rxbuf[4] = {0};
    
    {
        SPIFLASH_CS_LOW();        /* 使能片选 */

        txbuf[0] = SPIFLASH_CMD_RDID;   /* 读芯片ID命令 */

        /* 启动SPI数据传输 */
        spi_transfer(txbuf, rxbuf, 4);

        id1 = rxbuf[1];     /* ID的第1个字节 */
        id2 = rxbuf[2];     /* ID的第2个字节 */
        id3 = rxbuf[3];     /* ID的第3个字节 */

        SPIFLASH_CS_HIGH();        /* 失能片选 */
    }

    /* 组装ID值 */
    id = ((uint32_t) id1 << 16) | ((uint32_t) id2 << 8) | id3;

    return id;
}

/* 获得 spiflash 芯片特性 */
static HW_STATUS spiflash_get_attributte(void)
{
    HW_STATUS is_checked = HW_FALSE;

    /* 获得 SPIFLASH 的ID信息 */
    s_spiflash_attr.chip_id = spiflash_get_id();

    switch (s_spiflash_attr.chip_id)
    {
        case SPIFLASH_W25Q64BV_ID:
            strcpy(s_spiflash_attr.chip_name, "W25Q64BV");
            s_spiflash_attr.total_size  = 8 * 1024 * 1024;    /* 总容量 = 8M */
            s_spiflash_attr.bank_size   = 64 * 1024;         /* 块大小 = 64K */
            s_spiflash_attr.sector_size = 4 * 1024;        /* 扇区大小 = 4K */
            s_spiflash_attr.page_size   = 256;              /* 页大小 = 256B */

            is_checked = HW_TRUE;
            break;

        case SPIFLASH_W25Q128_ID:
            strcpy(s_spiflash_attr.chip_name, "W25Q128");
            s_spiflash_attr.total_size  = 16 * 1024 * 1024;    /* 总容量 = 16M */
            s_spiflash_attr.bank_size   = 64 * 1024;         /* 块大小 = 64K */
            s_spiflash_attr.sector_size = 4 * 1024;        /* 扇区大小 = 4K */
            s_spiflash_attr.page_size   = 256;              /* 页大小 = 256B */

            is_checked = HW_TRUE;
            break;

        default:
            is_checked = HW_FALSE;
            break;
    }

    return is_checked;
}

/* --------------------------------------------------------------------- */

/* spiflash 初始化 */
int spiflash_init(void)
{
    /* 获得 spiflash 芯片特性 */
    if (spiflash_get_attributte() == HW_FALSE)
    {
        return -1;
    }

    return 0;
}

/* 擦除指定的扇区 */
static void spiflash_sector_erase(uint32_t sector_addr)
{
    uint8_t  txbuf[4] = {0}, rxbuf[1];

    spiflash_write_enable();  /* 发送写使能命令 */
    
    {
        SPIFLASH_CS_LOW();        /* 使能片选 */

        txbuf[0] = SPIFLASH_CMD_SE;                     /* 发送擦除命令 */
        txbuf[1] = ((sector_addr & 0xFF0000) >> 16);    /* 发送扇区地址的高8bit */
        txbuf[2] = ((sector_addr & 0xFF00) >> 8);       /* 发送扇区地址中间8bit */
        txbuf[3] = (sector_addr & 0xFF);                /* 发送扇区地址低8bit */

        /* 启动SPI数据传输 */
        spi_transfer(txbuf, rxbuf, 4);

        SPIFLASH_CS_HIGH();        /* 失能片选 */
    }

    /* 等待串行Flash内部写操作完成 */
    spiflash_wait_write_end();
}

/* 按页写入数据 */
static void spiflash_page_write(uint8_t *buf, uint32_t addr, uint16_t size)
{
    uint8_t  txbuf[4 + 256] = {0};
    uint8_t  rxbuf[1];
    uint16_t len = 0;
    int      i;

    /* 大于1页字节大小, 禁止写入 */
    if ((size == 0) || (size >= s_spiflash_attr.page_size))
    {
        return;
    }

    /* 发送写使能命令 */
    spiflash_write_enable();

    /* 写入一页数据 */
    {
        SPIFLASH_CS_LOW();        /* 使能片选 */

        len = 0;
        txbuf[len++] = SPIFLASH_CMD_PAGEP;          /* 发送页编程命令 */
        txbuf[len++] = ((addr & 0xFF0000) >> 16);   /* 发送地址的高8bit */
        txbuf[len++] = ((addr & 0xFF00) >> 8);      /* 发送地址中间8bit */
        txbuf[len++] = (addr & 0xFF);               /* 发送地址低8bit */

        for (i = 0; i < s_spiflash_attr.page_size; i++)
        {
            if (i < size)
            {
                txbuf[len++] = buf[i];    /* 填充数据 */
            }
            else
            {
                txbuf[len++] = 0x00;      /* 填充 0x00 */
            }
        }

        /* 启动SPI数据传输 */
        spi_transfer(txbuf, rxbuf, len);

        SPIFLASH_CS_HIGH();        /* 失能片选 */
    }

    /* 进入写保护状态 */
    {
        SPIFLASH_CS_LOW();        /* 使能片选 */

        txbuf[0] = SPIFLASH_CMD_DISEWR;             /* 禁止写, 退出AAI状态 */
        
        /* 启动SPI数据传输 */
        spi_transfer(txbuf, rxbuf, 1);

        SPIFLASH_CS_HIGH();        /* 失能片选 */
    }

    /* 等待串行Flash内部写操作完成 */
    spiflash_wait_write_end();
}

/* 按页读取数据 */
static void spiflash_page_read(uint8_t *buf, uint32_t addr, uint16_t size)
{
    uint8_t  txbuf[4 + 256] = {0};
    uint8_t  rxbuf[256];
    uint16_t len = 0;

    /* 读取地址不能超出最大地址 */
    if ((size == 0) || ((addr + size) >= s_spiflash_attr.total_size))
    {
        return;
    }

    /* 进入读数据状态 */
    {
        SPIFLASH_CS_LOW();        /* 使能片选 */

        len = 0;
        txbuf[len++] = SPIFLASH_CMD_RDDT;             /* 读数据 */
        txbuf[len++] = ((addr & 0xFF0000) >> 16);     /* 发送地址的高8bit */
        txbuf[len++] = ((addr & 0xFF00) >> 8);        /* 发送地址中间8bit */
        txbuf[len++] = (addr & 0xFF);                 /* 发送地址低8bit */
        
        /* 启动SPI数据传输 */
        spi_transfer(txbuf, rxbuf, len);
    }

    /* 读出一页数据 */
    {
        len = s_spiflash_attr.page_size;

        /* 启动SPI数据传输 */
        spi_transfer(txbuf, rxbuf, len);

        SPIFLASH_CS_HIGH();        /* 失能片选 */
    }

    /* 复制数据 */
    memcpy(buf, rxbuf, len);
}

/* ***************** 向 mboot的数据存储 提供接口 ***************** */

/* 分配一片空间 */
static hw_addr_attr * spiflash_op_for_mboot_take_free_space(uint32_t size);
/* 擦除这片空间 */
static HW_STATUS spiflash_op_for_mboot_erase_space(uint32_t addr);
/* 写数据 */
static int spiflash_op_for_mboot_put_data(hw_buf_attr * buf_attr, uint32_t addr);
/* 读数据 */
static hw_buf_attr * spiflash_op_for_mboot_get_data(uint32_t addr, uint16_t nbr);

/* ***************** 提供给mboot的数据存储接口实现 ***************** */
/* 大空闲空间地址 */
#define SPIFLASH_FREE_BANK_IDX    10                        /*  从第10个Bank开始*/
#define SPIFLASH_FREE_ADDR        (SPIFLASH_FREE_BANK_IDX * 64 * 1024)  /* 对应地址 */
#define SPIFLASH_FREE_MAX_SIZE    (2 * 1024 * 1024)         /* 最大允许分配的空间 */

/* 空闲地址信息 */
hw_addr_attr spiflash_free_addr;

/* 保存数据的buf */
uint8_t spiflash_rd_buf[256], spiflash_wr_buf[256];
hw_buf_attr spiflash_write_buf = 
{
    .buf = spiflash_wr_buf,
    .len = 256,
};

hw_buf_attr spiflash_read_buf = 
{
    .buf = spiflash_rd_buf,
    .len = 256,
};


/* spiflash提供给 mboot的操作函数 */
spiflash_op_for_mboot s_spiflash_op_for_mboot = 
{
    .take_free_space  = spiflash_op_for_mboot_take_free_space,
    .erase_space      = spiflash_op_for_mboot_erase_space,
    .put_data         = spiflash_op_for_mboot_put_data,
    .get_data         = spiflash_op_for_mboot_get_data,
    .memory_mapped    = NULL,
};

/* 分配一片空间 */
static hw_addr_attr * spiflash_op_for_mboot_take_free_space(uint32_t size)
{
    int pnbr;

    /* 空间起始地址 */
    spiflash_free_addr.addr = SPIFLASH_FREE_ADDR;  /* 空闲空间的起始地址 */

    /* 按整数扇区分配 */
    pnbr = (size / s_spiflash_attr.sector_size);

    if ((size % s_spiflash_attr.sector_size))
    {
        pnbr++;
    }

    /* 大空闲空间长度 */
    spiflash_free_addr.len  = (pnbr * s_spiflash_attr.sector_size);

    return &spiflash_free_addr;
}

/* 擦除这片空间 */
static HW_STATUS spiflash_op_for_mboot_erase_space(uint32_t addr)
{
    int      i, snbr;

    uint32_t eaddr = addr;    /* addr 必是大/小空间的起始地址 */

    /* 计算总扇区个数 */
    snbr = spiflash_free_addr.len / s_spiflash_attr.sector_size;

    /* 擦除这些扇区 */
    for (i = 0; i < snbr; i++)
    {
        spiflash_sector_erase(eaddr);
        eaddr += (s_spiflash_attr.sector_size * i);
    }

    return HW_TRUE;
}

/* 写数据 */
static int spiflash_op_for_mboot_put_data(hw_buf_attr * buf_attr, uint32_t addr)
{
    memset(spiflash_write_buf.buf, 0, spiflash_write_buf.len);
    memcpy(spiflash_write_buf.buf, buf_attr->buf, buf_attr->len);

    /* 按页写入数据 */
    spiflash_page_write(spiflash_write_buf.buf, addr, spiflash_write_buf.len);

    return 0;
}

/* 读数据 */
static hw_buf_attr * spiflash_op_for_mboot_get_data(uint32_t addr, uint16_t nbr)
{
    /* 清空 */
    memset(spiflash_read_buf.buf, 0, spiflash_read_buf.len);

    /* 按页读出数据 */
    spiflash_page_read(spiflash_read_buf.buf, addr, spiflash_read_buf.len);

    return &spiflash_read_buf;
}

/* ***************** 向 mboot的数据存储 提供接口和接收配置信息 ***************** */

spiflash_op_for_mboot *spiflash_op_for_mboot_register(void)
{
    /* 返回操作函数接口 */
    return &s_spiflash_op_for_mboot;
}
